home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / cvs / sprite / RCS / commit.c,v < prev    next >
Encoding:
Text File  |  1991-10-08  |  22.2 KB  |  850 lines

  1. head     1.4;
  2. branch   ;
  3. access   ;
  4. symbols  ;
  5. locks    ; strict;
  6. comment  @ * @;
  7.  
  8.  
  9. 1.4
  10. date     91.09.10.16.11.21;  author jhh;  state Exp;
  11. branches ;
  12. next     1.3;
  13.  
  14. 1.3
  15. date     91.08.20.12.58.32;  author jhh;  state Exp;
  16. branches ;
  17. next     1.2;
  18.  
  19. 1.2
  20. date     91.07.29.11.48.20;  author jhh;  state Exp;
  21. branches ;
  22. next     1.1;
  23.  
  24. 1.1
  25. date     91.07.11.13.02.05;  author jhh;  state Exp;
  26. branches ;
  27. next     ;
  28.  
  29.  
  30. desc
  31. @@
  32.  
  33.  
  34. 1.4
  35. log
  36. @added -q and -Q to commit command
  37. @
  38. text
  39. @#ifndef lint
  40. static char rcsid[] = "$Id: commit.c,v 1.3 91/08/20 12:58:32 jhh Exp $";
  41. #endif !lint
  42.  
  43. /*
  44.  *    Copyright (c) 1989, Brian Berliner
  45.  *
  46.  *    You may distribute under the terms of the GNU General Public License
  47.  *    as specified in the README file that comes with the CVS 1.0 kit.
  48.  *
  49.  * Commit Files
  50.  *
  51.  *    "commit" commits the present version to the RCS repository, AFTER
  52.  *    having done a test on conflicts.  The call is:
  53.  *        cvs commit [options] files...
  54.  *
  55.  *    "commit" accepts the following options:
  56.  *        -f        Force a commit, even if the RCS $Id string
  57.  *                is not found
  58.  *        -n        Causes "commit" to *not* run any commit prog
  59.  *        -a        Commits all files in the current directory
  60.  *                that have been modified.
  61.  *        -m 'message'    Does not start up the editor for the
  62.  *                log message; just gleans it from the
  63.  *                'message' argument.
  64.  *        -r Revision    Allows committing to a particular *numeric*
  65.  *                revision number.
  66.  *
  67.  *    Note that "commit" does not do a recursive commit.  You must do
  68.  *    "commit" in each directory where there are files that you'd
  69.  *    like to commit.
  70.  */
  71.  
  72. #include <sys/param.h>
  73. #include <sys/types.h>
  74. #include <sys/stat.h>
  75. #include <ctype.h>
  76. #include "cvs.h"
  77.  
  78. static int force_commit_no_rcsid = 0;
  79.  
  80. extern int run_module_prog;
  81.  
  82. commit(argc, argv)
  83.     int argc;
  84.     char *argv[];
  85. {
  86.     int commit_all = 0, err = 0;
  87.     char *rev = "";            /* not NULL! */
  88.     char line[MAXLINELEN], message[MAXMESGLEN];
  89.     int c;
  90.  
  91.     if (argc == -1)
  92.     commit_usage();
  93.     /*
  94.      * For log purposes, do not allow "root" to commit files
  95.      */
  96.     if (geteuid() == 0)
  97.     error(0, "cannot commit files as 'root'");
  98.     optind = 1;
  99.     while ((c = getopt(argc, argv, "fnam:r:qQ")) != -1) {
  100.     switch (c) {
  101.     case 'f':
  102.         force_commit_no_rcsid = 1;
  103.         break;
  104.     case 'n':
  105.         run_module_prog = 0;
  106.         break;
  107.     case 'a':
  108.         commit_all = 1;
  109.         break;
  110.     case 'm':
  111.         use_editor = FALSE;
  112.         if (strlen(optarg) >= sizeof(message)) {
  113.         warn(0, "warning: message too long; truncated!");
  114.         (void) strncpy(message, optarg, sizeof(message));
  115.         message[sizeof(message) - 1] = '\0';
  116.         } else
  117.         (void) strcpy(message, optarg);
  118.         break;
  119.     case 'r':
  120.         if (!isdigit(optarg[0]))
  121.         error(0, "specified revision %s must be numeric!", optarg);
  122.         rev = optarg;
  123.         break;
  124.     case 'Q':
  125.         really_quiet = 1;
  126.         /* FALL THROUGH */
  127.     case 'q':
  128.         quiet = 1;
  129.         break;
  130.     case '?':
  131.     default:
  132.         commit_usage();
  133.         break;
  134.     }
  135.     }
  136.     argc -= optind;
  137.     argv += optind;
  138.     if (!commit_all && argc == 0)
  139.     error(0, "must specify the files you'd like to check-in");
  140.     if (commit_all && argc != 0)
  141.     error(0, "cannot specify files with the -a option");
  142.     Name_Repository();
  143.     Writer_Lock();
  144.     if (commit_all) {
  145.     Find_Names(&fileargc, fileargv, ALL);
  146.     argc = fileargc;
  147.     argv = fileargv;
  148.     }
  149.     if (rev[0] != '\0') {
  150.     register int i;
  151.     FILE *fptty;
  152.  
  153. #ifdef sprite
  154.     fptty = open_file(getenv("TTY"), "r");
  155. #else
  156.     fptty = open_file("/dev/tty", "r");
  157. #endif
  158.     printf("WARNING:\n");
  159.     printf("\tCommitting with a specific revision number\n");
  160.     printf("\tbypasses all consistency checks.  Are you abosulutely\n");
  161.     printf("\tsure you want to continue (y/n) [n] ? ");
  162.     (void) fflush(stdout);
  163.     if (fgets(line, sizeof(line), fptty) == NULL ||
  164.         (line[0] != 'y' && line[0] != 'Y')) {
  165.         error(0, "commit of revision %s aborted", rev);
  166.     }
  167.     (void) fclose(fptty);
  168.     /*
  169.      * When committing with a specific revision number, we simply
  170.      * fudge the lists that Collect_Sets() would have created for
  171.      * us.  This is all so gross, but sometimes useful.
  172.      */
  173.     Clist[0] = Glist[0] = Mlist[0] = Olist[0] = Dlist[0] = '\0';
  174.     Alist[0] = Rlist[0] = Wlist[0] = Llist[0] = Blist[0] = '\0';
  175.     for (i = 0; i < argc; i++) {
  176.         (void) strcat(Mlist, " ");
  177.         (void) strcat(Mlist, argv[i]);
  178.     }
  179.     } else {
  180.     err += Collect_Sets(argc, argv);
  181.     }
  182.     if (err == 0) {
  183.     err += commit_process_lists(message, rev);
  184.     if (err == 0 && run_module_prog) {
  185.         char *cp;
  186.         FILE *fp;
  187.  
  188.         /*
  189.          * It is not an error if Checkin.prog does not exist.
  190.          */
  191.         if ((fp = fopen(CVSADM_CIPROG, "r")) != NULL) {
  192.         if (fgets(line, sizeof(line), fp) != NULL) {
  193.             if ((cp = rindex(line, '\n')) != NULL)
  194.             *cp = '\0';
  195.             (void) sprintf(prog, "%s %s", line, Repository);
  196.             printf("%s %s: Executing '%s'\n", progname, command, prog);
  197.             (void) system(prog);
  198.         }
  199.         (void) fclose(fp);
  200.         }
  201.     }
  202.     Update_Logfile(Repository, message);
  203.     }
  204.     Lock_Cleanup(0);
  205.     exit(err);
  206. }
  207.  
  208. /*
  209.  * Process all the lists, returning the number of errors found.
  210.  */
  211. static
  212. commit_process_lists(message, rev)
  213.     char *message;
  214.     char *rev;
  215. {
  216.     char line[MAXLISTLEN], fname[MAXPATHLEN], revision[50];
  217.     FILE *fp;
  218.     char *cp;
  219.     int first, err = 0;
  220.  
  221.     /*
  222.      * Doesn't make much sense to commit a directory...
  223.      */
  224.     if (Dlist[0])
  225.     warn(0, "committing directories ignored -%s", Dlist);
  226.     /*
  227.      * Is everything up-to-date?
  228.      * Only if Glist, Olist, and Wlist are all NULL!
  229.      */
  230.     if (Glist[0] || Olist[0] || Wlist[0]) {
  231.     (void) fprintf(stderr, "%s: the following files are not ", progname);
  232.     (void) fprintf(stderr,
  233.                "up to date; use '%s update' first:\n", progname);
  234.     if (Glist[0] != '\0')
  235.         (void) fprintf(stderr, "\t%s\n", Glist);
  236.     if (Olist[0] != '\0')
  237.         (void) fprintf(stderr, "\t%s\n", Olist);
  238.     if (Wlist[0] != '\0')
  239.         (void) fprintf(stderr, "\t%s\n", Wlist);
  240.     Lock_Cleanup(0);
  241.     exit(1);
  242.     }
  243.     /*
  244.      * Is there anything to do in the first place?
  245.      */
  246.     if (Mlist[0] == '\0' && Rlist[0] == '\0' && Alist[0] == '\0') {
  247.     if (!quiet) {
  248.         error(0, "there is nothing to commit!");
  249.     } else {
  250.         Lock_Cleanup(0);
  251.         exit(1);
  252.     }
  253.     }
  254.     /*
  255.      * First we make sure that the file has an RCS $Id string in it
  256.      * and if it does not, the user is prompted for verification to continue.
  257.      */
  258.     if (force_commit_no_rcsid == 0) {
  259.     (void) strcpy(line, Mlist);
  260.     (void) strcat(line, Alist);
  261.     for (first = 1, cp = strtok(line, " \t"); cp;
  262.         cp = strtok((char *)NULL, " \t")) {
  263.         (void) sprintf(prog, "%s -s %s %s", GREP, RCSID_PAT, cp);
  264.         if (system(prog) != 0) {
  265.         if (first) {
  266.             printf("%s %s: WARNING!\n", progname, command);
  267. #ifndef sprite
  268.             printf("\tThe following file(s) do not contain an RCS $Id keyword:\n");
  269. #else
  270.             printf(
  271.     "\tThe following file(s) do not contain an RCS $Id or $Header keyword:\n");
  272. #endif
  273.             first = 0;
  274.         }
  275.         printf("\t\t%s\n", cp);
  276.         }
  277.     }
  278.     if (first == 0) {
  279. #ifdef sprite
  280.         FILE *fptty = open_file(getenv("TTY"), "r");
  281. #else
  282.         FILE *fptty = open_file("/dev/tty", "r");
  283. #endif
  284.         printf("\tAre you sure you want to continue (y/n) [n] ? ");
  285.         (void) fflush(stdout);
  286.         if (fgets(line, sizeof(line), fptty) == NULL ||
  287.         (line[0] != 'y' && line[0] != 'Y')) {
  288.         error(0, "commit aborted");
  289.         }
  290.         (void) fclose(fptty);
  291.     }
  292.     }
  293.     if (use_editor)
  294.     do_editor(message);
  295.     /*
  296.      * Mlist is the "modified, needs committing" list
  297.      */
  298.     (void) strcpy(line, Mlist);
  299.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  300.     (void) strcpy(User, cp);
  301.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  302.     if (lock_RCS(rev) != 0)
  303.         err++;
  304.     }
  305.     /*
  306.      * Rlist is the "to be removed" list
  307.      */
  308.     (void) strcpy(line, Rlist);
  309.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  310.     (void) strcpy(User, cp);
  311.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  312.     if (lock_RCS(rev) != 0)
  313.         err++;
  314.     }
  315.     /*
  316.      * Alist is the "to be added" list
  317.      */
  318.     (void) strcpy(line, Alist);
  319.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  320.     (void) strcpy(User, cp);
  321.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  322.     (void) sprintf(prog, "%s/%s -i -t%s/%s%s", Rcsbin, RCS, CVSADM,
  323.                User, CVSEXT_LOG);
  324.     (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_OPT);
  325.     fp = open_file(fname, "r");
  326.     while (fgets(fname, sizeof(fname), fp) != NULL) {
  327.         if ((cp = rindex(fname, '\n')) != NULL)
  328.         *cp = '\0';
  329.         (void) strcat(prog, " ");
  330.         (void) strcat(prog, fname);
  331.     }
  332.     (void) fclose(fp);
  333.     (void) strcat(prog, " ");
  334.     (void) strcat(prog, Rcs);
  335.     if (system(prog) == 0) {
  336.         fix_rcs_modes(Rcs, User);
  337.     } else {
  338.         warn(0, "could not create %s", Rcs);
  339.         err++;
  340.     }
  341.     }
  342.     /*
  343.      * If something failed, release all locks and restore the default
  344.      * branches
  345.      */
  346.     if (err) {
  347.     int didllist = 0;
  348.     char *branch;
  349.  
  350.     for (cp = strtok(Llist, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  351.         didllist = 1;
  352.         (void) strcpy(User, cp);
  353.         (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  354.         (void) sprintf(prog, "%s/%s -q -u %s", Rcsbin, RCS, Rcs);
  355.         if (system(prog) != 0)
  356.         warn(0, "could not UNlock %s", Rcs);
  357.     }
  358.     if (didllist) {
  359.         for (cp=strtok(Blist, " \t"); cp; cp=strtok((char *)NULL, " \t")) {
  360.         if ((branch = rindex(cp, ':')) == NULL)
  361.             continue;
  362.         *branch++ = '\0';
  363.         (void) strcpy(User, cp);
  364.         (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  365.         (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS,
  366.                    branch, Rcs);
  367.         if (system(prog) != 0)
  368.             warn(0, "could not restore branch %s to %s", branch, Rcs);
  369.         }
  370.     }
  371.     for (cp = strtok(Alist, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  372.         (void) strcpy(User, cp);
  373.         (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  374.         (void) unlink(Rcs);
  375.     }
  376.     Lock_Cleanup(0);
  377.     exit(1);
  378.     }
  379.     /*
  380.      * Got them all, now go ahead;
  381.      * First, add the files in the Alist
  382.      */
  383.     if (Alist[0] != '\0') {
  384.     int maxrev, rev;
  385.  
  386.     /* scan the entries file looking for the max revision number */
  387.     fp = open_file(CVSADM_ENT, "r");
  388.     maxrev = 0;
  389.     while (fgets(line, sizeof(line), fp) != NULL) {
  390.         rev = atoi(line);
  391.         if (rev > maxrev)
  392.         maxrev = rev;
  393.     }
  394.     if (maxrev == 0)
  395.         maxrev = 1;
  396.     (void) fclose(fp);
  397.     (void) sprintf(revision, "-r%d", maxrev);
  398.     (void) strcpy(line, Alist);
  399.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  400.         (void) strcpy(User, cp);
  401.         if (Checkin(revision, message) != 0)
  402.         err++;
  403.         (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_OPT);
  404.         (void) unlink(fname);
  405.         (void) sprintf(fname, "%s/%s%s", CVSADM, User, CVSEXT_LOG);
  406.         (void) unlink(fname);
  407.     }
  408.     }
  409.     /*
  410.      * Everyone else uses the head as it is set in the RCS file,
  411.      * or the revision that was specified on the command line.
  412.      */
  413.     if (rev[0] != '\0')
  414.     (void) sprintf(revision, "-r%s", rev);
  415.     else
  416.     revision[0] = '\0';
  417.     /*
  418.      * Commit the user modified files in Mlist
  419.      */
  420.     (void) strcpy(line, Mlist);
  421.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  422.     (void) strcpy(User, cp);
  423.     if (Checkin(revision, message) != 0)
  424.         err++;
  425.     }
  426.     /*
  427.      * And remove the RCS files in Rlist, by placing it in the Attic
  428.      */
  429.     (void) strcpy(line, Rlist);
  430.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  431.     int omask;
  432.  
  433.     (void) strcpy(User, cp);
  434.     (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
  435.     (void) sprintf(fname, "%s/%s", Repository, CVSATTIC);
  436.     omask = umask(2);
  437.     (void) mkdir(fname, 0777);
  438.     (void) umask(omask);
  439.     (void) sprintf(fname, "%s/%s/%s%s", Repository, CVSATTIC,
  440.                User, RCSEXT);
  441.     (void) sprintf(prog, "%s/%s -u -q %s", Rcsbin, RCS, Rcs);
  442.     if ((system(prog) == 0 && rename(Rcs, fname) != -1) ||
  443.         (!isreadable(Rcs) && isreadable(fname)))
  444.         Scratch_Entry(User);
  445.     else
  446.         err++;
  447.     }
  448.     return (err);
  449. }
  450.  
  451. /*
  452.  * Attempt to place a lock on the RCS file; returns 0 if it could and
  453.  * 1 if it couldn't.  If the RCS file currently has a branch as the head,
  454.  * we must move the head back to the trunk before locking the file, and
  455.  * be sure to put the branch back as the head if there are any errors.
  456.  */
  457. static
  458. lock_RCS(rev)
  459.     char *rev;
  460. {
  461.     char branch[50];
  462.     int err = 0;
  463.  
  464.     branch[0] = '\0';
  465.     /*
  466.      * For a specified, numeric revision of the form "1" or "1.1",
  467.      * (or when no revision is specified ""), definitely move the
  468.      * branch to the trunk before locking the RCS file.
  469.      *
  470.      * The assumption is that if there is more than one revision
  471.      * on the trunk, the head points to the trunk, not a branch...
  472.      * and as such, it's not necessary to move the head in this case.
  473.      */
  474.     if (numdots(rev) < 2) {
  475.     branch_number(Rcs, branch);
  476.     if (branch[0] != '\0') {
  477.         (void) sprintf(prog, "%s/%s -q -b %s", Rcsbin, RCS, Rcs);
  478.         if (system(prog) != 0) {
  479.         warn(0, "cannot change branch to default for %s", Rcs);
  480.         return (1);
  481.         }
  482.     }
  483.     (void) sprintf(prog, "%s/%s -q -l %s", Rcsbin, RCS, Rcs);
  484.     err = system(prog);
  485.     } else {
  486.     (void) sprintf(prog, "%s/%s -q -l%s %s 2>%s",
  487.                Rcsbin, RCS, rev, Rcs, DEVNULL);
  488.     (void) system(prog);
  489.     }
  490.     if (err == 0) {
  491.     (void) strcat(Llist, " ");
  492.     (void) strcat(Llist, User);
  493.     (void) strcat(Blist, " ");
  494.     (void) strcat(Blist, User);
  495.     if (branch[0] != '\0') {
  496.         (void) strcat(Blist, ":");
  497.         (void) strcat(Blist, branch);
  498.     }
  499.     return (0);
  500.     }
  501.     if (branch[0] != '\0') {
  502.     (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS, branch, Rcs);
  503.     if (system(prog) != 0)
  504.         warn(0, "cannot restore branch to %s for %s", branch, Rcs);
  505.     }
  506.     return (1);
  507. }
  508.  
  509. /*
  510.  * A special function used only by lock_RCS() to determine if the current
  511.  * head is pointed at a branch.  Returns the result in "branch" as a null
  512.  * string if the trunk is the head, or as the branch number if the branch
  513.  * is the head.
  514.  */
  515. static
  516. branch_number(rcs, branch)
  517.     char *rcs;
  518.     char *branch;
  519. {
  520.     char line[MAXLINELEN];
  521.     FILE *fp;
  522.     char *cp;
  523.  
  524.     branch[0] = '\0';            /* Assume trunk is head */
  525.     fp = open_file(rcs, "r");
  526.     if (fgets(line, sizeof(line), fp) == NULL) {
  527.     (void) fclose(fp);
  528.     return;
  529.     }
  530.     if (fgets(line, sizeof(line), fp) == NULL) {
  531.     (void) fclose(fp);
  532.     return;
  533.     }
  534.     (void) fclose(fp);
  535.     if (strncmp(line, RCSBRANCH, sizeof(RCSBRANCH) - 1) != 0 ||
  536.     !isspace(line[sizeof(RCSBRANCH) - 1]) ||
  537.     (cp = rindex(line, ';')) == NULL)
  538.     return;
  539.     *cp = '\0';                /* strip the ';' */
  540.     if ((cp = rindex(line, ' ')) == NULL &&
  541.     (cp = rindex(line, '\t')) == NULL)
  542.     return;
  543.     cp++;
  544.     if (*cp == NULL)
  545.     return;
  546.     (void) strcpy(branch, cp);
  547. }
  548.  
  549. /*
  550.  * Puts a standard header on the output which is either being prepared for
  551.  * an editor session, or being sent to a logfile program.  The modified, added,
  552.  * and removed files are included (if any) and formatted to look pretty.
  553.  */
  554. static
  555. setup_tmpfile(fp, prefix)
  556.     FILE *fp;
  557.     char *prefix;
  558. {
  559.     if (Mlist[0] != '\0') {
  560.     (void) fprintf(fp, "%sModified Files:\n", prefix);
  561.     fmt(fp, Mlist, prefix);
  562.     }
  563.     if (Alist[0] != '\0') {
  564.     (void) fprintf(fp, "%sAdded Files:\n", prefix);
  565.     fmt(fp, Alist, prefix);
  566.     }
  567.     if (Rlist[0] != '\0') {
  568.     (void) fprintf(fp, "%sRemoved Files:\n", prefix);
  569.     fmt(fp, Rlist, prefix);
  570.     }
  571. }
  572.  
  573. /*
  574.  * Breaks the files list into reasonable sized lines to avoid line
  575.  * wrap...  all in the name of pretty output.
  576.  */
  577. static
  578. fmt(fp, instring, prefix)
  579.     FILE *fp;
  580.     char *instring;
  581.     char *prefix;
  582. {
  583.     char line[MAXLINELEN];
  584.     char *cp;
  585.     int col;
  586.  
  587.     (void) strcpy(line, instring);    /* since strtok() is destructive */
  588.     (void) fprintf(fp, "%s\t", prefix);
  589.     col = 8;                /* assumes that prefix is < 8 chars */
  590.     for (cp = strtok(line, " \t"); cp; cp = strtok((char *)NULL, " \t")) {
  591.     if ((col + strlen(cp)) > 70) {
  592.         (void) fprintf(fp, "\n%s\t", prefix);
  593.         col = 8;
  594.     }
  595.     (void) fprintf(fp, "%s ", cp);
  596.     col += strlen(cp) + 1;
  597.     }
  598.     (void) fprintf(fp, "\n%s\n", prefix);
  599. }
  600.  
  601. /*
  602.  * Builds a temporary file using setup_tmpfile() and invokes the user's
  603.  * editor on the file.  The header garbage in the resultant file is then
  604.  * stripped and the log message is stored in the "message" argument.
  605.  */
  606. static
  607. do_editor(message)
  608.     char *message;
  609. {
  610.     FILE *fp;
  611.     char line[MAXLINELEN], fname[MAXPATHLEN];
  612.     int fd;
  613.  
  614.     message[0] = '\0';
  615.     (void) strcpy(fname, CVSTEMP);
  616.     if ((fd = mkstemp(fname)) < 0)
  617.     error(0, "cannot create temporary file %s", fname);
  618.     if ((fp = fdopen(fd, "w+")) == NULL)
  619.     error(0, "cannot create FILE * to %s", fname);
  620.     setup_tmpfile(fp, CVSEDITPREFIX);
  621.     (void) fprintf(fp, "%sEnter Log.  Lines beginning with '%s' are removed automatically\n",
  622.            CVSEDITPREFIX, CVSEDITPREFIX);
  623.     (void) fprintf(fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX);
  624.     (void) fclose(fp);
  625.     (void) sprintf(prog, "%s %s", Editor, fname);
  626.     if (system(prog) != 0)
  627.     warn(0, "warning: editor session failed");
  628.     fp = open_file(fname, "r");
  629.     while (fgets(line, sizeof(line), fp) != NULL) {
  630.     if (strncmp(line, CVSEDITPREFIX, sizeof(CVSEDITPREFIX)-1) == 0)
  631.         continue;
  632.     if ((strlen(message) + strlen(line)) >= MAXMESGLEN) {
  633.         warn(0, "warning: log message truncated!");
  634.         break;
  635.     }
  636.     (void) strcat(message, line);
  637.     }
  638.     (void) fclose(fp);
  639.     (void) unlink(fname);
  640. }
  641.  
  642. /*
  643.  * Uses setup_tmpfile() to pass the updated message on directly to
  644.  * any logfile programs that have a regular expression match for the
  645.  * checked in directory in the source repository.  The log information
  646.  * is fed into the specified program as standard input.
  647.  */
  648. Update_Logfile(repository, message)
  649.     char *repository;
  650.     char *message;
  651. {
  652.     FILE *fp_info;
  653.     char logfile[MAXPATHLEN], title[MAXLISTLEN+MAXPATHLEN], line[MAXLINELEN];
  654.     char path[MAXPATHLEN], default_filter[MAXLINELEN];
  655.     char *exp, *filter, *cp, *short_repository;
  656.     int filter_run, line_number;
  657.  
  658.     if (CVSroot == NULL) {
  659.     warn(0, "CVSROOT variable not set; no log message will be sent");
  660.     return;
  661.     }
  662.     (void) sprintf(logfile, "%s/%s", CVSroot, CVSROOTADM_LOGINFO);
  663.     if ((fp_info = fopen(logfile, "r")) == NULL) {
  664.     warn(0, "warning: cannot open %s", logfile);
  665.     return;
  666.     }
  667.     if (CVSroot != NULL)
  668.     (void) sprintf(path, "%s/", CVSroot);
  669.     else
  670.     (void) strcpy(path, REPOS_STRIP);
  671.     if (strncmp(repository, path, strlen(path)) == 0)
  672.     short_repository = repository + strlen(path);
  673.     else
  674.     short_repository = repository;
  675.     (void) sprintf(title, "'%s%s'", short_repository, Llist);
  676.     default_filter[0] = '\0';
  677.     filter_run = line_number = 0;
  678.     while (fgets(line, sizeof(line), fp_info) != NULL) {
  679.     line_number++;
  680.     if (line[0] == '#')
  681.         continue;
  682.     for (cp = line; *cp && isspace(*cp); cp++)
  683.         ;
  684.     if (*cp == '\0')
  685.         continue;            /* blank line */
  686.     for (exp = cp; *cp && !isspace(*cp); cp++)
  687.         ;
  688.     if (*cp != '\0')
  689.         *cp++ = '\0';
  690.     while (*cp && isspace(*cp))
  691.         cp++;
  692.     if (*cp == '\0') {
  693.         warn(0, "syntax error at line %d file %s; ignored",
  694.          line_number, logfile);
  695.         continue;
  696.     }
  697.     filter = cp;
  698.     if ((cp = rindex(filter, '\n')) != NULL)
  699.         *cp = '\0';            /* strip the newline */
  700.     /*
  701.      * At this point, exp points to the regular expression, and
  702.      * filter points to the program to exec.  Evaluate the regular
  703.      * expression against short_repository and exec the filter
  704.      * if it matches.
  705.      */
  706.     if (strcmp(exp, "DEFAULT") == 0) {
  707.         (void) strcpy(default_filter, filter);
  708.         continue;
  709.     }
  710.     /*
  711.      * For a regular expression of "ALL", send the log message
  712.      * to the requested filter *without* noting that a filter was run.
  713.      * This allows the "DEFAULT" regular expression to be more
  714.      * meaningful with all updates going to a master log file.
  715.      */
  716.     if (strcmp(exp, "ALL") == 0) {
  717.         (void) logfile_write(repository, filter, title, message);
  718.         continue;
  719.     }
  720.     if ((cp = re_comp(exp)) != NULL) {
  721.         warn(0, "bad regular expression at line %d file %s: %s",
  722.          line_number, logfile, cp);
  723.         continue;
  724.     }
  725.     if (re_exec(short_repository) == 0)
  726.         continue;            /* no match */
  727.     if (logfile_write(repository, filter, title, message) == 0)
  728.         filter_run = 1;
  729.     }
  730.     if (filter_run == 0 && default_filter[0] != '\0')
  731.     (void) logfile_write(repository, default_filter, title, message);
  732. }
  733.  
  734. /*
  735.  * Since some systems don't define this...
  736.  */
  737. #ifndef MAXHOSTNAMELEN
  738. #define    MAXHOSTNAMELEN    64
  739. #endif !MAXHOSTNAMELEN
  740.  
  741. /*
  742.  * Writes some stuff to the logfile "filter" and returns the status of the
  743.  * filter program.
  744.  */
  745. static
  746. logfile_write(repository, filter, title, message)
  747.     char *repository;
  748.     char *filter;
  749.     char *title;
  750.     char *message;
  751. {
  752.     char cwd[MAXPATHLEN], host[MAXHOSTNAMELEN];
  753.     FILE *fp;
  754.     char *cp;
  755.  
  756.     /*
  757.      * A maximum of 6 %s arguments are supported in the filter
  758.      */
  759.     (void) sprintf(prog, filter, title, title, title, title, title, title);
  760.     if ((fp = popen(prog, "w")) == NULL) {
  761.     warn(0, "cannot write entry to log filter: %s", prog);
  762.     return (1);
  763.     }
  764.     if (gethostname(host, sizeof(host)) < 0)
  765.     (void) strcpy(host, "(unknown)");
  766.     (void) fprintf(fp, "Update of %s\n", repository);
  767.     (void) fprintf(fp, "In directory %s:%s\n\n", host,
  768.            (cp = getwd(cwd)) ? cp : cwd);
  769.     setup_tmpfile(fp, "");
  770.     (void) fprintf(fp, "Log Message:\n%s\n", message);
  771.     return (pclose(fp));
  772. }
  773.  
  774. /*
  775.  * Called when "add"ing files to the RCS respository, as it is necessary
  776.  * to preserve the file modes in the same fashion that RCS does.  This would
  777.  * be automatic except that we are placing the RCS ,v file very far away from
  778.  * the user file, and I can't seem to convince RCS of the location of the
  779.  * user file.  So we munge it here, after the ,v file has been successfully
  780.  * initialized with "rcs -i".
  781.  */
  782. static
  783. fix_rcs_modes(rcs, user)
  784.     char *rcs;
  785.     char *user;
  786. {
  787.     struct stat sb;
  788.  
  789.     if (stat(user, &sb) != -1) {
  790.     (void) chmod(rcs, (int) sb.st_mode & ~0222);
  791.     }
  792. }
  793.  
  794. static
  795. commit_usage()
  796. {
  797.     (void) fprintf(stderr,
  798.     "%s %s [-fn] [-a] [-m 'message'] [-r revision] [files...]\n",
  799.            progname, command);
  800.     exit(1);
  801. }
  802. @
  803.  
  804.  
  805. 1.3
  806. log
  807. @allow $Header in files
  808. @
  809. text
  810. @d2 1
  811. a2 1
  812. static char rcsid[] = "$Id: commit.c,v 1.2 91/07/29 11:48:20 jhh Exp $";
  813. d61 1
  814. a61 1
  815.     while ((c = getopt(argc, argv, "fnam:r:")) != -1) {
  816. d86 6
  817. d208 8
  818. a215 2
  819.     if (Mlist[0] == '\0' && Rlist[0] == '\0' && Alist[0] == '\0')
  820.     error(0, "there is nothing to commit!");
  821. @
  822.  
  823.  
  824. 1.2
  825. log
  826. @sprite doesn't have /dev/tty
  827. @
  828. text
  829. @d2 1
  830. a2 1
  831. static char rcsid[] = "$Id: commit.c,v 1.1 91/07/11 13:02:05 jhh Exp Locker: jhh $";
  832. d217 1
  833. d219 4
  834. @
  835.  
  836.  
  837. 1.1
  838. log
  839. @Initial revision
  840. @
  841. text
  842. @d2 1
  843. a2 1
  844. static char rcsid[] = "$Id: commit.c,v 1.28.1.2 91/01/29 07:16:59 berliner Exp $";
  845. d109 3
  846. d113 1
  847. d224 3
  848. d228 1
  849. @
  850.